#include "native/java/lang/Class.hpp"
#include "rtda/heap/class.h"
#include "rtda/heap/string_pool.h"

namespace jvm {
    namespace lang {

        const std::string ClassNative::JL_CLASS = "java/lang/Class";

        void ClassNative::getPrimitiveClass(std::shared_ptr<rtda::Frame> frame) {
            auto&& nameObj = frame->getLocalVars().getRef(0);
            auto name = heap::StringPool::getInstance().CPPString(nameObj);

            auto loader = frame->getMethod()->getClass()->getLoder();
            auto clazz = loader->loadClass(name)->getJClass();

            frame->getOperandStack()->pushRef(clazz);
        }

        void ClassNative::getName0(std::shared_ptr<rtda::Frame> frame) {
            auto thisObj = frame->getLocalVars().getThis();
            auto extra = thisObj->getExtra();
            std::shared_ptr<heap::Class> pclass;
            try
            {
                pclass = std::any_cast<std::shared_ptr<heap::Class>>(extra);
            }
            catch (const std::bad_any_cast& e)
            {
                LOG_ERROR("Not a Class pointer");
                throw;
            }

            auto name = pclass->getJavaName();
            auto nameObj = heap::StringPool::getInstance().JString(pclass->getLoder(), name);

            frame->getOperandStack()->pushRef(nameObj);
        }

        void ClassNative::desiredAssertionStatus0(std::shared_ptr<rtda::Frame> frame) {
            frame->getOperandStack()->pushBoolean(false);
        }

        void ClassNative::isInterface(std::shared_ptr<rtda::Frame> frame) {
            auto thisObj = frame->getLocalVars().getThis();
            auto&& extra = thisObj->getExtra();
            std::shared_ptr<heap::Class> pclass;
            try
            {
                pclass = std::any_cast<std::shared_ptr<heap::Class>>(extra);
            }
            catch (const std::bad_any_cast& e)
            {
                LOG_ERROR("not a class pointer");
                throw;
            }
            frame->getOperandStack()->pushBoolean(pclass->isInterface());
        }

        void ClassNative::isPrimitive(std::shared_ptr<rtda::Frame> frame) {
            auto thisObj = frame->getLocalVars().getThis();
            auto&& extra = thisObj->getExtra();
            std::shared_ptr<heap::Class> pclass;
            try
            {
                pclass = std::any_cast<std::shared_ptr<heap::Class>>(extra);
            }
            catch (const std::bad_any_cast& e)
            {
                LOG_ERROR("not a class pointer");
                throw;
            }
            frame->getOperandStack()->pushBoolean(pclass->isPrimitive());
        }

        void ClassNative::getSuperclass(std::shared_ptr<rtda::Frame> frame)
        {
            auto thisObj = frame->getLocalVars().getThis();
            //auto&& pclass = thisObj->getClass().lock();
            std::shared_ptr<heap::Class> pclass;
            try
            {
                pclass = std::any_cast<std::shared_ptr<heap::Class>>(thisObj->getExtra());
            }
            catch (const std::bad_any_cast& e)
            {
                LOG_ERROR("not a class pointer!");
                throw;
            }
            if (!pclass)
            {
                LOG_ERROR("null pointer!");
                throw std::runtime_error("null pointer!");
            }

            auto&& p_spclass = pclass->getSuperClass();
            if (!p_spclass)
            {
                frame->getOperandStack()->pushRef(nullptr);
                //LOG_ERROR("null pointer!");
                //throw std::runtime_error("null pointer!");
            }
            else
            {
                auto clazz = p_spclass->getJClass();
                frame->getOperandStack()->pushRef(clazz);
            }
        }


    } // namespace lang
} // namespace jvm